/**
 * <AiRmob-Framework; A simple augmented reality framework for android >
    Copyright (C) <2012>  <AiRmob Team>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/* 


 * Renderer of the AiRmob-Engine. 
 * All other components of the engine are known to the renderer and therefore can be communicated to through the renderer.
 * 
 * Rendercomponents by Axel Peter
 * 
 * Shaders (located in assets) by Philipp Brandt
 * 
 */

package rendercomponents;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.LinkedList;
import java.lang.Math;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import airmob.framework.CameraMatrizes;
import android.content.Context;
import android.graphics.Bitmap;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.os.SystemClock;

public class Renderer extends AiRmobRenderer {

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// /Global variables
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// Scene (= Parent/Group) that will be given to the sceneManagment to go
	// through the scene graph
	protected Scene scene;
	ResourceManager resourceManager = new ResourceManager(this);
	SceneManager sceneManager = new SceneManager(this);
	public Settings settings;

	// Fixed shaders
	VertexShader pickingVertexShader;
	FragmentShader pickingFragmentShader;

	VertexShader depthVertexShader;
	FragmentShader depthFragmentShader;

	VertexShader textVertexShader;
	FragmentShader textFragmentShader;

	VertexShader outlineVertexShader;
	FragmentShader outlineFragmentShader;

	VertexShader blurVertexShader;
	FragmentShader blurFragmentShader;

	VertexShader bloomVertexShader;
	FragmentShader bloomFragmentShader;

	// Animation variables
	float globalTime = 0;
	long startTime = 0;
	boolean measureTimeForAnimation = false;
	int frameCount = 0;

	// RBO, FBO, returning Identifier and boolean for picking
	int[] pickingFBOHandle;
	int[] pickingRBOHandle;
	int[] pickingDepthRBOHandle;
	protected Identifier pickingID;
	boolean picked;
	boolean waitforpick = true;
	int pickX, pickY = -1;
	Identifier pickingIdentifier;

	// Variables for depthTextures
	private int[] depthTexture;
	private int[] depthFBO;
	private int[] depthRBO;
	LinkedList<Texture> shadowTexture;

	// Variables for Imagepostprocessing
	private int[] ippTexture;
	private int[] ippFBO;
	private int[] ippRBO;
	Texture postProcessingTexture;
	private int[] bloomTexture;
	private int[] bloomFBO;
	private int[] bloomRBO;
	Texture bloomResultTexture;
	private float[] fullScreenQuadVertices = { -1, -1, 0, 0, 0, 1, -1, 0, 1, 0,
			1, 1, 0, 1, 1, -1, 1, 0, 0, 1 };
	private short[] fullScreenQuadIndices = { 0, 1, 3, 1, 2, 3 };

	private FloatBuffer fullScreenQuadvBuffer;
	private ShortBuffer fullScreenQuadiBuffer;

	private int fullscreenQuadVertexBuffer;
	private int fullScreenQuadIndexBuffer;
	private int numFullScreenQuadIndices;

	float gaussDirection;

	// Variables for camera-to-background
	protected boolean bgTexBMPChanged = false;
	protected Bitmap bgTexBMP;
	Texture BGTexture;

	// All used matrices (object transformation matrices are stored in the
	// respective object)
	public float[] mViewMatrix = new float[16];
	public float[] mProjectionMatrix = new float[16];
	float[] mProjectionViewMatrix = new float[16];
	float[] mMVPMatrix = new float[16];
	float[] mTransformationMatrix = new float[16];
	float[] mGlobalTransformationMatrix = new float[16];

	// Shaderhandles
	int vertexShader;
	int fragmentShader;
	int shadowmapShader;
	int mProgram;
	int mFullScreenProgram;
	int mOutlineProgram;
	int mPickingProgram;
	int mDepthProgram;
	int mTextProgram;
	int mBlurProgram;
	int mBloomProgram;

	// Attribute-, uniform- and texturehandles
	int aPositionHandle;
	int aNormalHandle;
	// As shaders do not use color this is obsolete and not used anymore
	// int aColorHandle;
	int aTextureHandle;
	int mTextureID;
	int[] textures;

	// Matrixhandles
	int uMVPMatrixHandle;
	int uGlobalTransformationMatrixHandle;
	int uViewMatrixHandle;
	int uInvertedViewMatrixHandle;

	// Width, height, far and near of the view-frustum
	float near = 0.1f;
	float far = 800;
	int width;
	int height;

	// Used for FPS-Measuring
	private int mFrameCount = 0;
	private long mStartTime = System.nanoTime();
	double inMSeconds = 0;
	long startDraw = 0;

	// Different offset-, stride-, and formatsizes
	private static final int FLOAT_SIZE_BYTES = 4;
	private static final int SHORT_SIZE_BYTES = 2;
	private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITH_TEXTURE_WITH_NORMAL = 8 * FLOAT_SIZE_BYTES;
	private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_TEXTURE_WITHOUT_NORMAL = 6 * FLOAT_SIZE_BYTES;
	private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_NORMALS_WITH_TEXTURE = 5 * FLOAT_SIZE_BYTES;
	private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_NORMALS_WITHOUT_TEXTURE = 3 * FLOAT_SIZE_BYTES;
	private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
	private static final int TRIANGLE_VERTICES_DATA_NOR_OFFSET = 3;
	private static int TRIANGLE_VERTICES_DATA_TEX_OFFSET = 6;

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// /Constructor and other functions in other locations to work with the
	// renderer
	// /all public functions (with the exception of setCameraLookAt)!
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// /Constructor
	// ////////////////////////////////////////////////////////

	/**
	 * Constructor. Generates a renderer. Requires the android-context.
	 */
	public Renderer(Context context) {
		super(context);
		this.resourceManager = new ResourceManager(this);
		this.sceneManager = new SceneManager(this);
		this.settings = new Settings();
	}

	// ////////////////////////////////////////////////////////
	// /Function to be overwritten by the used scene
	// ////////////////////////////////////////////////////////

	/**
	 * Has to be overwritten to use a scene with this renderer.
	 */
	public void putScene() {
	}

	/**
	 * 
	 * @return the used scene in this renderer.
	 */
	public Scene getScene() {
		return this.scene;
	}

	/**
	 * 
	 * @param scene
	 *            the scene to use in this renderer.
	 */
	public void setScene(Scene scene) {
		this.scene = scene;
	}

	// ////////////////////////////////////////////////////////
	// Not-working camera-to-background function
	// ////////////////////////////////////////////////////////

	/**
	 * Used to set the background to the image taken by the camera. Should not
	 * be used elsewise.
	 */
	public void setVideoBackground(Bitmap bmp) {
		if (settings.cameraTex) {
			if (surfaceCreatedDone) {
				bgTexBMP = bmp;
				bgTexBMPChanged = true;
			}
		}
	}

	// ////////////////////////////////////////////////////////
	// Returns Identifier at given position, returns null if no Identifier was
	// found
	// ////////////////////////////////////////////////////////

	/**
	 * Tries to pick a model at the screencoordinates x and y.
	 */
	public Identifier pick(int x, int y) {
		picked = false;
		pickX = x;
		pickY = y;

		return pickingIdentifier;
	}

	// ////////////////////////////////////////////////////////
	// Returns the wether picking is activated or not from the settings
	// ////////////////////////////////////////////////////////

	/**
	 * Returns the wether picking is activated or not.
	 */
	public boolean pickingActivated() {
		return settings.activatePicking;
	}

	// ////////////////////////////////////////////////////////
	// Sets the settings for cameraBg.
	// ////////////////////////////////////////////////////////

	/**
	 * Sets the settings for cameraBg.
	 */
	public void freezeCamera(boolean freeze) {
		this.settings.cameraTex = freeze;
	}

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// Android-Functions used to communicate with openGL
	// onSurfaceCreated: is called once on startup and imports all objects
	// used in the scene and sets up all shaders and buffers required
	//
	// onSurfaceChanged: called whenever the surface changes (i.e. when
	// the device is turned around) and once on Startup. Adjusts width and
	// height and all buffers and textures that use these variables
	//
	// onDrawFrame: draw-routine called on every frame to draw the scene
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// Called ones on startup
	// ////////////////////////////////////////////////////////

	public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {

		if (settings.measureFPS) {
			startDraw = System.nanoTime();
		}

		// At this point all necessary imports for the given scene are done
		this.putScene();

		// Set initial values for fullScreenQuads
		this.setFullScreenQuad();
		
		//If there will be only one light, use the more efficient shader
		if (settings.maxLights == 1){
			settings.mainVertexShaderSource = "shader/onelight_main.vert";
			settings.mainFragmentShaderSource = "shader/onelight_main.frag";
		}

		// Main Shader
		VertexShader vertexShader = new VertexShader(
				settings.mainVertexShaderSource);
		FragmentShader fragmentShader = new FragmentShader(
				settings.mainFragmentShaderSource);
		this.mProgram = resourceManager.createProgram(vertexShader,
				fragmentShader);

		// Textshaders
		textVertexShader = new VertexShader(settings.textVertexShaderSource);
		textFragmentShader = new FragmentShader(
				settings.textFragmentShaderSource);
		this.mTextProgram = resourceManager.createProgram(textVertexShader,
				textFragmentShader);

		// Set Viewmatrix if tracking is deactivated
		if (!settings.tracking) {
			Matrix.setLookAtM(mViewMatrix, 0, -75f, 0f, 25f, 0f, 0f, 0f, 0f,
					0f, 1f);
		}

		// Build the scenegraph
		sceneManager.buildSceneGraph();
		this.scene.somethingChanged = false;

		// Picking program
		if (settings.activatePicking) {
			pickingVertexShader = new VertexShader(
					settings.pickingVertexShaderSource);
			pickingFragmentShader = new FragmentShader(
					settings.pickingFragmentShaderSource);
			this.mPickingProgram = resourceManager.createProgram(
					pickingVertexShader, pickingFragmentShader);
		}

		// Outline program
		if (settings.drawWithOutline) {
			outlineVertexShader = new VertexShader(
					settings.outlineVertexShaderSource);
			outlineFragmentShader = new FragmentShader(
					settings.outlineFragmentShaderSource);
			this.mOutlineProgram = resourceManager.createProgram(
					outlineVertexShader, outlineFragmentShader);
		}

		// DepthProgram for Shadowmaps
		if (settings.drawShadowMap) {
			depthVertexShader = new VertexShader(
					settings.depthVertexShaderSource);
			depthFragmentShader = new FragmentShader(
					settings.depthFragmentShaderSource);
			this.mDepthProgram = resourceManager.createProgram(
					depthVertexShader, depthFragmentShader);

			this.shadowTexture = new LinkedList<Texture>();

			for (int i = 0; i < settings.maxLights; i++) {
				shadowTexture.add(new Texture(0));
			}

		}

		// Postprocessing
		if (settings.postProcessing || settings.showShadowMap
				|| settings.cameraTex) {
			if (settings.postProcessing)
				postProcessingTexture = new Texture(0);

			if (settings.cameraTex) {
				int camTexId[] = new int[1];
				GLES20.glGenTextures(1, camTexId, 0);
				GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, camTexId[0]);

				GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
						GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
				GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
						GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
				GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
						GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
				GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
						GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
				this.BGTexture = new Texture(camTexId[0]);
			}

			VertexShader fullScreenVertexShader = new VertexShader(
					settings.fullScreenVertexShaderSource);
			FragmentShader fullScreenFragmentShader = new FragmentShader(
					settings.fullScreenFragmentShaderSource);
			this.mFullScreenProgram = resourceManager.createProgram(
					fullScreenVertexShader, fullScreenFragmentShader);

			if (settings.blur) {
				blurVertexShader = new VertexShader(
						settings.blurVertexShaderSource);
				blurFragmentShader = new FragmentShader(
						settings.blurFragmentShaderSource);
				this.mBlurProgram = resourceManager.createProgram(
						blurVertexShader, blurFragmentShader);
			}

			if (settings.bloom) {
				bloomVertexShader = new VertexShader(
						settings.bloomVertexShaderSource);
				bloomFragmentShader = new FragmentShader(
						settings.bloomFragmentShaderSource);
				this.mBloomProgram = resourceManager.createProgram(
						bloomVertexShader, bloomFragmentShader);

				bloomResultTexture = new Texture(0);
			}
		}

		// Set other stuff
		GLES20.glEnable(GLES20.GL_DEPTH_TEST);
		GLES20.glFrontFace(GLES20.GL_CCW);
		GLES20.glEnable(GLES20.GL_CULL_FACE);
		GLES20.glCullFace(GLES20.GL_BACK);
		GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

		this.surfaceCreatedDone = true;
		rendererCreatedDone();

		if (settings.measureFPS) {
			long endCreation = System.nanoTime();
			inMSeconds = (endCreation - startDraw) / 1.0e9;

			double msPerCreation = (1000 * inMSeconds);
			System.out.println("ms needed for surfaceCreated: " + msPerCreation
					+ ", in seconds: " + msPerCreation / 1000);

			inMSeconds = 0;
		}

		this.startTime = System.nanoTime();
	}

	// ////////////////////////////////////////////////////////
	// /Called on every surface change and once on startup
	// ////////////////////////////////////////////////////////

	public void onSurfaceChanged(GL10 glUnused, int width, int height) {
		this.width = width;
		this.height = height;
		GLES20.glViewport(0, 0, width, height);

		if (!settings.tracking) {
			float angleOfView = this.cameraAngleHorizontal;
			perspectiveProjectionNorm(mProjectionMatrix, near, far,
					angleOfView, height, width);
		}

		if (settings.activatePicking)
			setupPickingRendering();

		if (settings.drawShadowMap)
			setupShadowMap();

		if (settings.postProcessing)
			setupPostProcessing();

		rendererChangedDone();
	}

	// ////////////////////////////////////////////////////////
	// /Called on every frame
	// ////////////////////////////////////////////////////////

	public void onDrawFrame(GL10 glUnused) {

		if (measureTimeForAnimation) {
			this.globalTime = (float) (System.nanoTime() - startTime) / 1000000000;
		}

		if (settings.measureFPS) {
			startDraw = System.nanoTime();
		}

		Matrix.multiplyMM(mProjectionViewMatrix, 0, mProjectionMatrix, 0,
				mViewMatrix, 0);

		if (this.scene.somethingChanged) {
			sceneManager.buildSceneGraph();
			this.scene.somethingChanged = false;
		} else if (this.settings.sortObjectsByDistance) {
			sceneManager.sortSceneGraph();
		}

		if (settings.rotate) {
			long time = SystemClock.uptimeMillis() % 4000L;
			float angle = 0.090f * ((int) time);
			scene.setTransformationToIdentity();
			scene.rotate(angle, 1.0f, 1.0f, 0.0f);
		}

		// Picking
		if (settings.activatePicking) {
			if (pickX != -1 && pickY != -1) {
				drawPickingTexture();

				internalPick(pickX, pickY);
				pickX = -1;
				pickY = -1;
				setPicked(pickingIdentifier);
				pickingIdentifier = null;
			}
		}

		if (settings.drawShadowMap) {
			drawShadowMap();
			for (int i = 0; i < scene.numLights; i++)
				shadowTexture.get(i).ID = depthTexture[i];
		}

		if (settings.showShadowMap && settings.drawShadowMap) {
			GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
			setAllHandlers(mFullScreenProgram);
			GLES20.glUseProgram(mFullScreenProgram);
			drawFullScreenQuad(shadowTexture.get(settings.showSMNum),
					mFullScreenProgram);
		}

		if (!settings.showShadowMap) {

			GLES20.glEnable(GLES20.GL_DITHER);
			GLES20.glEnable(GLES20.GL_BLEND);
			GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA,
					GLES20.GL_ONE_MINUS_SRC_ALPHA);

			drawAllObjects(mProgram);

			if (settings.postProcessing) {
				postProcessing();
			}
		}

		if (settings.measureFPS) {

			long endDraw = System.nanoTime();
			inMSeconds += ((endDraw - startDraw) / 1.0e9);

			++mFrameCount;
			if (mFrameCount % 100 == 0) {
				long now = System.nanoTime();
				double elapsedS = (now - mStartTime) / 1.0e9;
				double msPerFrame = (1000 * elapsedS / mFrameCount);
				System.out.println("Synched: ms / frame: " + msPerFrame
						+ " - fps: " + (1000 / msPerFrame));
				lastSynchFrameRate = (int) (1000 / msPerFrame);

				double msPerFrameReal = (1000 * inMSeconds / mFrameCount);
				System.out.println("Real: ms / frame: " + msPerFrameReal
						+ " - fps: " + (1000 / msPerFrameReal));
				lastRealFrameRate = (int) (1000 / msPerFrameReal);

				inMSeconds = 0;
				mFrameCount = 0;
				mStartTime = now;
			}

			frameCount++;
		}
	}

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// /Functions to receive or generate view- and projectionmatrix
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// /Sets the viewMatrix (input comes from the tracker)
	// ////////////////////////////////////////////////////////

	@Override
	public boolean setMatrizes(CameraMatrizes cameraView) {
		if (settings.tracking) {
			mViewMatrix = cameraView.modelViewMatrix;
			mProjectionMatrix = cameraView.projectionMatrix;
			return true;
		}
		return false;
	}

	// ////////////////////////////////////////////////////////
	// /Calculates the perspective-projection-matrix
	// /Only used if tracking is deactivated
	// ////////////////////////////////////////////////////////

	private void perspectiveProjectionNorm(float[] matrix, float n, float f,
			float angleOfView, float H, float B) {

		float F = (float) (1 / (Math.tan(Math.toRadians(angleOfView)) / 2.0));

		// First Column
		matrix[0] = (H / B) * F;
		matrix[1] = 0.0f;
		matrix[2] = 0.0f;
		matrix[3] = 0.0f;

		// Second Column
		matrix[4] = 0.0f;
		matrix[5] = F;
		matrix[6] = 0.0f;
		matrix[7] = 0.0f;

		// Third Column
		matrix[8] = 0;
		matrix[9] = 0;
		matrix[10] = (f + n) / (n - f);
		matrix[11] = -1;

		// Fourth Column
		matrix[12] = 0.0f;
		matrix[13] = 0.0f;
		matrix[14] = ((2 * f * n) / (n - f));
		matrix[15] = 0.0f;

	}

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// /Functions used to draw all objects
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// /Draw all the models in this scene
	// ////////////////////////////////////////////////////////

	private void drawAllObjects(int usedProgram) {
		if (settings.postProcessing) {
			GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, ippFBO[0]);
			GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
					GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D,
					ippTexture[0], 0);
			GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER,
					GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER,
					ippRBO[0]);

			int statusFBO = GLES20
					.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
			if (statusFBO != GLES20.GL_FRAMEBUFFER_COMPLETE) {
				System.out
						.println("Framebuffer used for image postprocessing is incomplete!");
			}

		} else {
			GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
		}

		GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

		// Set camera image given by framework as background
		if (settings.cameraTex) {
			if (bgTexBMP != null && bgTexBMPChanged) {
				GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, BGTexture.ID);
				GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bgTexBMP, 0);
				bgTexBMPChanged = false;
				bgTexBMP.recycle();
			}

			// Because the y-coordinate is mirrored it has to be changed for
			// this fullscreenquad
			float[] swap = new float[20];
			for (int i = 0; i < 20; i++)
				swap[i] = fullScreenQuadVertices[i];
			this.fullScreenQuadVertices[4] = 1;
			this.fullScreenQuadVertices[9] = 1;
			this.fullScreenQuadVertices[14] = 0;
			this.fullScreenQuadVertices[19] = 0;
			this.setFullScreenQuad();
			drawFullScreenQuad(BGTexture, mFullScreenProgram);
			this.fullScreenQuadVertices = swap;
			this.setFullScreenQuad();
		}

		GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);

		// Draw outlines before the mainShader is used
		if (settings.drawWithOutline) {

			setAllHandlers(mOutlineProgram);
			GLES20.glUseProgram(mOutlineProgram);
			GLES20.glCullFace(GLES20.GL_FRONT);

			int locTextCol = GLES20.glGetUniformLocation(mTextProgram,
					"u_color");
			GLES20.glUniform4fv(locTextCol, 1, settings.outlineColor, 0);

			for (Identifier p : sceneManager.getAllObjects()) {
				if (!p.isVisible)
					continue;

				this.mTransformationMatrix = p.mTotalTransformationMatrix
						.clone();

				if (p.holdsModel3D) {
					this.drawModelForOutline(p.child, usedProgram);
				} else if (p.holdsModelGroup) {
					for (int j = 0; j < p.modelGroupChild.models.size(); j++) {
						this.drawModelForOutline(
								p.modelGroupChild.models.get(j), usedProgram);
					}
				} else if (p.holdsModelAnimation) {
					this.drawModelForOutline(this.getAnimationFrame(p),
							usedProgram);
				} else if (p.holdsText) {
					continue;
				}
			}
			GLES20.glCullFace(GLES20.GL_BACK);
			GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
		}

		setAllHandlers(usedProgram);
		GLES20.glUseProgram(usedProgram);

		boolean textSet = false;

		int num = 0;

		for (Identifier p : sceneManager.getAllLights()) {
			if (p.holdsLight) {
				this.drawLight(p.lightChild, usedProgram, num);
				num++;
			}
		}

		for (Identifier p : sceneManager.getAllObjects()) {
			if (!p.isVisible)
				continue;

			this.mTransformationMatrix = p.mTotalTransformationMatrix.clone();

			float[] lightMVP = new float[16];

			float ratio = width / height;
			float[] lightProjection = new float[16];
			Matrix.frustumM(lightProjection, 0, -ratio, ratio, -1, 1, 1f, 40000);

			float[] lightMV = new float[16];
			float[] mModelMatrix = new float[16];
			Matrix.setIdentityM(mModelMatrix, 0);

			Matrix.multiplyMM(mModelMatrix, 0, mTransformationMatrix, 0,
					mModelMatrix, 0);

			Matrix.multiplyMM(lightMV, 0,
					sceneManager.getAllLights().get(0).lightChild.getView(), 0,
					mModelMatrix, 0);

			Matrix.multiplyMM(lightMVP, 0, lightProjection, 0, lightMV, 0);

			int locMVP = GLES20.glGetUniformLocation(usedProgram,
					"l_mvp_matrix");
			GLES20.glUniformMatrix4fv(locMVP, 1, false, lightMVP, 0);

			if (p.holdsModel3D) {
				this.drawModel(p.child, usedProgram);
			} else if (p.holdsModelGroup) {
				for (int j = 0; j < p.modelGroupChild.models.size(); j++) {
					this.drawModel(p.modelGroupChild.models.get(j), usedProgram);
				}
			} else if (p.holdsModelAnimation) {
				this.drawModel(this.getAnimationFrame(p), usedProgram);

			} else if (p.holdsText) {
				if (!textSet) {
					setAllHandlers(mTextProgram);
					GLES20.glUseProgram(mTextProgram);
					textSet = true;
				}
				this.drawText(p.textChild);
			}
		}

		GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
	}

	// ////////////////////////////////////////////////////////
	// /Draws a Model3D
	// ////////////////////////////////////////////////////////

	private void drawModel(Model3D model, int usedProgram) {
		float[] mModelMatrix = new float[16];
		Matrix.setIdentityM(mModelMatrix, 0);

		Matrix.multiplyMM(mModelMatrix, 0, mTransformationMatrix, 0,
				mModelMatrix, 0);

		// MVPMatrix = (Projection * View) * ModelMatrix
		Matrix.setIdentityM(mMVPMatrix, 0);
		Matrix.multiplyMM(mMVPMatrix, 0, mProjectionViewMatrix, 0,
				mModelMatrix, 0);
		// Give MVP Matrix to shader
		GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, mMVPMatrix, 0);

		// ModelViewMatrix = View * ModelMatrix
		float[] mModelViewMatrix = new float[16];
		Matrix.setIdentityM(mModelViewMatrix, 0);
		Matrix.multiplyMM(mModelViewMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
		// Give ModelViewMatrix to shader
		GLES20.glUniformMatrix4fv(uViewMatrixHandle, 1, false,
				mModelViewMatrix, 0);

		// Transposed inverted ModelViewMatrix
		float[] mInvertedViewMatrix = new float[16];
		Matrix.setIdentityM(mInvertedViewMatrix, 0);
		Matrix.invertM(mInvertedViewMatrix, 0, mModelViewMatrix, 0);
		float[] mInvertedTransposedViewMatrix = new float[16];
		Matrix.setIdentityM(mInvertedTransposedViewMatrix, 0);
		Matrix.transposeM(mInvertedTransposedViewMatrix, 0,
				mInvertedViewMatrix, 0);

		// Give transposed inverted ModelViewMatrix to shader
		GLES20.glUniformMatrix4fv(uInvertedViewMatrixHandle, 1, false,
				mInvertedTransposedViewMatrix, 0);

		// System.out.println("Tests ---");
		// System.out.println("Test material values------");
		// System.out.println("Ambient:");
		// for (int i = 0; i < 4; i++){
		// System.out.print(model.getMaterialAmbient()[i] + ", ");
		// }
		// System.out.println(" ");
		// System.out.println("Diffuse:");
		// for (int i = 0; i < 4; i++){
		// System.out.print(model.getMaterialDiffuse()[i] + ", ");
		// }
		// System.out.println(" ");
		// System.out.println("Specular:");
		// for (int i = 0; i < 4; i++){
		// System.out.print(model.getMaterialSpecular()[i] + ", ");
		// }
		// System.out.println(" ");
		// System.out.println("Exponent: " + model.getMaterialExponent());
		// System.out.println("---------");

		// Give material colors to vertexshader
		if (settings.lightingMode == 1) {
			int locAmbient = GLES20.glGetUniformLocation(usedProgram,
					"u_material.ambient_color");
			GLES20.glUniform4fv(locAmbient, 1, model.getMaterialAmbient(), 0);

			int locDiffuse = GLES20.glGetUniformLocation(usedProgram,
					"u_material.diffuse_color");
			GLES20.glUniform4fv(locDiffuse, 1, model.getMaterialDiffuse(), 0);

			int locSpecular = GLES20.glGetUniformLocation(usedProgram,
					"u_material.specular_color");
			GLES20.glUniform4fv(locSpecular, 1, model.getMaterialSpecular(), 0);

			int locExponent = GLES20.glGetUniformLocation(usedProgram,
					"u_material.specular_exponent");
			GLES20.glUniform1f(locExponent, model.getMaterialExponent());
		} else if (settings.lightingMode == 2) {
			int locAmbient2 = GLES20.glGetUniformLocation(usedProgram,
					"v_material.ambient_color");
			GLES20.glUniform4fv(locAmbient2, 1, model.getMaterialAmbient(), 0);

			int locDiffuse2 = GLES20.glGetUniformLocation(usedProgram,
					"v_material.diffuse_color");
			GLES20.glUniform4fv(locDiffuse2, 1, model.getMaterialDiffuse(), 0);

			int locSpecular2 = GLES20.glGetUniformLocation(usedProgram,
					"v_material.specular_color");
			GLES20.glUniform4fv(locSpecular2, 1, model.getMaterialSpecular(), 0);

			int locExponent2 = GLES20.glGetUniformLocation(usedProgram,
					"v_material.specular_exponent");
			GLES20.glUniform1f(locExponent2, model.getMaterialExponent());
		} else if (settings.lightingMode == 3) {
			int locAmbient3 = GLES20.glGetUniformLocation(usedProgram,
					"v_material.ambient_color");
			GLES20.glUniform4fv(locAmbient3, 1, model.getMaterialAmbient(), 0);

			int locDiffuse3 = GLES20.glGetUniformLocation(usedProgram,
					"v_material.diffuse_color");
			GLES20.glUniform4fv(locDiffuse3, 1, model.getMaterialDiffuse(), 0);

			int locSpecular3 = GLES20.glGetUniformLocation(usedProgram,
					"v_material.specular_color");
			GLES20.glUniform4fv(locSpecular3, 1, model.getMaterialSpecular(), 0);

			int locExponent3 = GLES20.glGetUniformLocation(usedProgram,
					"v_material.specular_exponent");
			GLES20.glUniform1f(locExponent3, model.getMaterialExponent());
		}

		int uLightingLoc = GLES20.glGetUniformLocation(usedProgram,
				"u_lightingMode");
		GLES20.glUniform1f(uLightingLoc, settings.lightingMode);

		int lightToggle = 0;

		if (settings.useSpotlights)
			lightToggle = 1;

		int uSpotLoc = GLES20.glGetUniformLocation(usedProgram,
				"u_use_spotlights");
		GLES20.glUniform1i(uSpotLoc, lightToggle);

		int shadowToggle = 0;

		if (settings.drawShadowMap)
			shadowToggle = 1;

		int uMappingLoc = GLES20.glGetUniformLocation(usedProgram,
				"v_mappingMode");
		GLES20.glUniform1f(uMappingLoc, settings.mappingMode);

		int uShadowLoc = GLES20.glGetUniformLocation(usedProgram,
				"u_shadowToggle");
		GLES20.glUniform1i(uShadowLoc, shadowToggle);

		int STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITH_TEXTURE_WITH_NORMAL;

		if (!model.textureCoordsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_TEXTURE_WITHOUT_NORMAL;

		if (!model.normalsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_NORMALS_WITH_TEXTURE;

		if (!model.normalsSet && !model.textureCoordsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_NORMALS_WITHOUT_TEXTURE;

		if (!model.normalsSet && model.textureCoordsSet)
			TRIANGLE_VERTICES_DATA_TEX_OFFSET = 3;
		else
			TRIANGLE_VERTICES_DATA_TEX_OFFSET = 6;

		int vLightingLoc = GLES20.glGetUniformLocation(usedProgram,
				"v_lightingMode");
		GLES20.glUniform1f(vLightingLoc, settings.lightingMode);

		if (settings.mappingMode != 0 && model.bumpMapSet) {
			int normalMap = GLES20.glGetUniformLocation(usedProgram,
					"normalMap");
			GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, model.bumpMap.ID);
			GLES20.glUniform1i(normalMap, 2);
		}

		if (settings.drawShadowMap) {
			for (int i = 0; i < scene.numLights; i++) {
				int shadowTex = 0;
				if (i == 0)
					shadowTex = GLES20.glGetUniformLocation(usedProgram,
							"shadowTexture");
				else
					shadowTex = GLES20.glGetUniformLocation(usedProgram,
							"shadowTexture" + i);

				if (i == 0)
					GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
				else if (i == 1)
					GLES20.glActiveTexture(GLES20.GL_TEXTURE3);
				else if (i == 2)
					GLES20.glActiveTexture(GLES20.GL_TEXTURE4);
				else if (i == 3)
					GLES20.glActiveTexture(GLES20.GL_TEXTURE5);
				else if (i == 4)
					GLES20.glActiveTexture(GLES20.GL_TEXTURE6);
				else if (i == 5)
					GLES20.glActiveTexture(GLES20.GL_TEXTURE7);
				else if (i == 6)
					GLES20.glActiveTexture(GLES20.GL_TEXTURE8);
				else if (i == 7)
					GLES20.glActiveTexture(GLES20.GL_TEXTURE9);

				GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,
						shadowTexture.get(i).ID);
				GLES20.glUniform1i(shadowTex, 1);
			}
		}

		if (!settings.useVBO) {
			// Give texCoords to vertexshader
			model.mesh.bVertexBuffer
					.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
			GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT,
					false, STRIDE_SIZE, model.mesh.bVertexBuffer);
			GLES20.glEnableVertexAttribArray(aPositionHandle);

			if (model.normalsSet) {
				// Give normals to vertexshader
				model.mesh.bVertexBuffer
						.position(TRIANGLE_VERTICES_DATA_NOR_OFFSET);
				GLES20.glVertexAttribPointer(aNormalHandle, 3, GLES20.GL_FLOAT,
						false, STRIDE_SIZE, model.mesh.bVertexBuffer);
				GLES20.glEnableVertexAttribArray(aNormalHandle);
			}

			if (model.textureCoordsSet && model.textureSet) {
				int texture = GLES20.glGetUniformLocation(usedProgram,
						"sTexture");
				GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
				GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, model.texture.ID);
				GLES20.glUniform1i(texture, 0);

				// Give texCoords to vertexshader
				model.mesh.bVertexBuffer
						.position(TRIANGLE_VERTICES_DATA_TEX_OFFSET);
				GLES20.glVertexAttribPointer(aTextureHandle, 2,
						GLES20.GL_FLOAT, false, STRIDE_SIZE,
						model.mesh.bVertexBuffer);
				GLES20.glEnableVertexAttribArray(aTextureHandle);
			}

			// As shaders do not use color this is obsolete and not used anymore
			// if (model.useColor) {
			// // Give colors to vertexshader
			// GLES20.glVertexAttribPointer(aColorHandle, 4, GLES20.GL_FLOAT,
			// false, COLOR_DATA_STRIDE_BYTES, model.bColorBuffer);
			// GLES20.glEnableVertexAttribArray(aColorHandle);
			// }

			// Draw everything
			GLES20.glDrawElements(GLES20.GL_TRIANGLES,
					model.bIndexBuffer.capacity(), GLES20.GL_UNSIGNED_SHORT,
					model.bIndexBuffer);
		} else {

			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, model.mesh.vBuffer);

			GLES20.glEnableVertexAttribArray(0);
			GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false,
					STRIDE_SIZE, TRIANGLE_VERTICES_DATA_POS_OFFSET);

			if (model.normalsSet) {
				GLES20.glEnableVertexAttribArray(1);
				GLES20.glVertexAttribPointer(1, 3, GLES20.GL_FLOAT, false,
						STRIDE_SIZE, FLOAT_SIZE_BYTES
								* TRIANGLE_VERTICES_DATA_NOR_OFFSET);
			}

			if (model.textureCoordsSet && model.textureSet) {

				int texture = GLES20.glGetUniformLocation(usedProgram,
						"sTexture");
				GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
				GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, model.texture.ID);
				GLES20.glUniform1i(texture, 0);

				GLES20.glEnableVertexAttribArray(2);
				GLES20.glVertexAttribPointer(2, 2, GLES20.GL_FLOAT, false,
						STRIDE_SIZE, FLOAT_SIZE_BYTES
								* TRIANGLE_VERTICES_DATA_TEX_OFFSET);
			}

			// As shaders do not use color this is obsolete and not used anymore
			// if (model.useColor) {
			// GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, model.cBuffer);
			// GLES20.glEnableVertexAttribArray(3);
			//
			// GLES20.glVertexAttribPointer(3, 4, GLES20.GL_FLOAT, false,
			// 4 * 4, 0);
			//
			// }

			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, model.iBuffer);
			GLES20.glDrawElements(GLES20.GL_TRIANGLES, model.numIndices,
					GLES20.GL_UNSIGNED_SHORT, 0);

			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
		}
	}

	// ////////////////////////////////////////////////////////
	// /Draws a light
	// ////////////////////////////////////////////////////////

	private void drawLight(Light light, int usedProgram, int num) {

		float[] halfplane = light.getPosition().clone();
		halfplane[2] += 1;
		VectorMethods.normalize(halfplane);

		if (settings.lightingMode == 1) {
			int numLoc = GLES20
					.glGetUniformLocation(usedProgram, "u_light_num");
			GLES20.glUniform1i(numLoc, scene.numLights);

			int locAmbient = GLES20.glGetUniformLocation(usedProgram,
					"u_global_ambient_color");
			GLES20.glUniform4fv(locAmbient, 1, settings.globalAmbientColor, 0);

			if (num == 0) {
				int locDirection = GLES20.glGetUniformLocation(usedProgram,
						"u_light.direction");
				GLES20.glUniform3fv(locDirection, 1, light.getDirection(), 0);

				int locHalf = GLES20.glGetUniformLocation(usedProgram,
						"u_light.halfplane");
				GLES20.glUniform3fv(locHalf, 1, halfplane, 0);

				int locPosition = GLES20.glGetUniformLocation(usedProgram,
						"u_light.position");
				GLES20.glUniform3fv(locPosition, 1, light.getPosition(), 0);

				int locExponent = GLES20.glGetUniformLocation(usedProgram,
						"u_light.exponent");
				GLES20.glUniform1f(locExponent, light.getExponent());

				int locAngle = GLES20.glGetUniformLocation(usedProgram,
						"u_light.angle");
				GLES20.glUniform1f(locAngle, light.getAngle());

				int locDiffuse = GLES20.glGetUniformLocation(usedProgram,
						"u_light.diffuse_color");
				GLES20.glUniform4fv(locDiffuse, 1, light.getLightDiffuse(), 0);

				int locSpecular = GLES20.glGetUniformLocation(usedProgram,
						"u_light.specular_color");
				GLES20.glUniform4fv(locSpecular, 1, light.getLightSpecular(), 0);
			} else if (num >= 1) {
				int locDirection = GLES20.glGetUniformLocation(usedProgram,
						"u_light" + num + ".direction");
				GLES20.glUniform3fv(locDirection, 1, light.getDirection(), 0);

				int locHalf = GLES20.glGetUniformLocation(usedProgram,
						"u_light" + num + ".halfplane");
				GLES20.glUniform3fv(locHalf, 1, halfplane, 0);

				int locPosition = GLES20.glGetUniformLocation(usedProgram,
						"u_light" + num + ".position");
				GLES20.glUniform3fv(locPosition, 1, light.getPosition(), 0);

				int locExponent = GLES20.glGetUniformLocation(usedProgram,
						"u_light" + num + ".exponent");
				GLES20.glUniform1f(locExponent, light.getExponent());

				int locAngle = GLES20.glGetUniformLocation(usedProgram,
						"u_light" + num + ".angle");
				GLES20.glUniform1f(locAngle, light.getAngle());

				int locDiffuse = GLES20.glGetUniformLocation(usedProgram,
						"u_light" + num + ".diffuse_color");
				GLES20.glUniform4fv(locDiffuse, 1, light.getLightDiffuse(), 0);

				int locSpecular = GLES20.glGetUniformLocation(usedProgram,
						"u_light" + num + ".specular_color");
				GLES20.glUniform4fv(locSpecular, 1, light.getLightSpecular(), 0);
			}

		} else if (settings.lightingMode == 2 || settings.lightingMode == 3) {
			int numLoc = GLES20
					.glGetUniformLocation(usedProgram, "v_light_num");
			GLES20.glUniform1i(numLoc, scene.numLights);

			int locAmbient = GLES20.glGetUniformLocation(usedProgram,
					"v_global_ambient_color");
			GLES20.glUniform4fv(locAmbient, 1, settings.globalAmbientColor, 0);

			if (num == 0) {
				int locDirection2 = GLES20.glGetUniformLocation(usedProgram,
						"v_light.direction");
				GLES20.glUniform3fv(locDirection2, 1, light.getDirection(), 0);

				int locHalf2 = GLES20.glGetUniformLocation(usedProgram,
						"v_light.halfplane");
				GLES20.glUniform3fv(locHalf2, 1, halfplane, 0);

				int locPosition2 = GLES20.glGetUniformLocation(usedProgram,
						"v_light.position");
				GLES20.glUniform3fv(locPosition2, 1, light.getPosition(), 0);

				int locExponent2 = GLES20.glGetUniformLocation(usedProgram,
						"v_light.exponent");
				GLES20.glUniform1f(locExponent2, light.getExponent());

				int locAngle2 = GLES20.glGetUniformLocation(usedProgram,
						"v_light.angle");
				GLES20.glUniform1f(locAngle2, light.getAngle());

				int locDiffuse2 = GLES20.glGetUniformLocation(usedProgram,
						"v_light.diffuse_color");
				GLES20.glUniform4fv(locDiffuse2, 1, light.getLightDiffuse(), 0);

				int locSpecular2 = GLES20.glGetUniformLocation(usedProgram,
						"v_light.specular_color");
				GLES20.glUniform4fv(locSpecular2, 1, light.getLightSpecular(),
						0);
			} else if (num >= 1) {
				int locDirection2 = GLES20.glGetUniformLocation(usedProgram,
						"v_light" + num + ".direction");
				GLES20.glUniform3fv(locDirection2, 1, light.getDirection(), 0);

				int locHalf2 = GLES20.glGetUniformLocation(usedProgram,
						"v_light" + num + ".halfplane");
				GLES20.glUniform3fv(locHalf2, 1, halfplane, 0);

				int locPosition2 = GLES20.glGetUniformLocation(usedProgram,
						"v_light" + num + ".position");
				GLES20.glUniform3fv(locPosition2, 1, light.getPosition(), 0);

				int locExponent2 = GLES20.glGetUniformLocation(usedProgram,
						"v_light" + num + ".exponent");
				GLES20.glUniform1f(locExponent2, light.getExponent());

				int locAngle2 = GLES20.glGetUniformLocation(usedProgram,
						"v_light" + num + ".angle");
				GLES20.glUniform1f(locAngle2, light.getAngle());

				int locDiffuse2 = GLES20.glGetUniformLocation(usedProgram,
						"v_light" + num + ".diffuse_color");
				GLES20.glUniform4fv(locDiffuse2, 1, light.getLightDiffuse(), 0);

				int locSpecular2 = GLES20.glGetUniformLocation(usedProgram,
						"v_light" + num + ".specular_color");
				GLES20.glUniform4fv(locSpecular2, 1, light.getLightSpecular(),
						0);
			}
		}
	}

	// ////////////////////////////////////////////////////////
	// /Setup all handlers for usedProgram
	// ////////////////////////////////////////////////////////

	private void setAllHandlers(int usedProgram) {
		// Set Handlers for Attributes
		if (!settings.useVBO) {
			aPositionHandle = GLES20.glGetAttribLocation(usedProgram,
					"a_position");
			aNormalHandle = GLES20.glGetAttribLocation(usedProgram, "a_normal");
			// As shaders do not use color this is obsolete and not used anymore
			// aColorHandle = GLES20.glGetAttribLocation(usedProgram,
			// "a_color");
			aTextureHandle = GLES20.glGetAttribLocation(usedProgram, "a_tc");
		} else {
			GLES20.glBindAttribLocation(usedProgram, 0, "a_position");
			GLES20.glBindAttribLocation(usedProgram, 1, "a_normal");
			GLES20.glBindAttribLocation(usedProgram, 2, "a_tc");
			// As shaders do not use color this is obsolete and not used anymore
			// GLES20.glBindAttribLocation(usedProgram, 3, "a_color");
		}

		// Set Handlers for Uniforms
		uMVPMatrixHandle = GLES20.glGetUniformLocation(usedProgram,
				"u_mvp_matrix");
		uViewMatrixHandle = GLES20.glGetUniformLocation(usedProgram,
				"u_modelview_matrix");
		uInvertedViewMatrixHandle = GLES20.glGetUniformLocation(usedProgram,
				"u_inv_modelview_matrix");

		GLES20.glLinkProgram(usedProgram);
	}

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// /Functions for picking -
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	private void internalPick(int x, int y) {
		GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, pickingFBOHandle[0]);

		int ID = 0;

		// If picking is not extended just pick one pixel
		if (settings.extendedPicking <= 0) {
			ByteBuffer pixel = ByteBuffer.allocateDirect(4).order(
					ByteOrder.nativeOrder());

			GLES20.glReadPixels(x, height - y, 1, 1, GLES20.GL_RGBA,
					GLES20.GL_UNSIGNED_BYTE, pixel);

			pixel.position(0);

			int[] b = new int[4];
			for (int i = 0; i < 4; i++)
				b[i] = (pixel.get(i) & 0xFF);

			for (int i = 0; i < 4; i++) {
				b[i] = (b[i]) / 17;
			}

			int red = b[0];
			int green = b[1] * 16;
			int blue = b[2] * 255;

			ID = red + green + blue;
		}
		// If picking is extended read multiple pixels
		else {
			// First test if there is something directly under the clicked
			// coordinates (because then there is no need for further work)
			ByteBuffer pixel = ByteBuffer.allocateDirect(4).order(
					ByteOrder.nativeOrder());

			GLES20.glReadPixels(x, height - y, 1, 1, GLES20.GL_RGBA,
					GLES20.GL_UNSIGNED_BYTE, pixel);

			pixel.position(0);

			int[] bP = new int[4];
			for (int i = 0; i < 4; i++)
				bP[i] = (pixel.get(i) & 0xFF);

			for (int i = 0; i < 4; i++) {
				bP[i] = (bP[i]) / 17;
			}

			int red = bP[0];
			int green = bP[1] * 16;
			int blue = bP[2] * 255;

			ID = red + green + blue;

			// If nothing was found for the one pixel go on with extendedPicking
			if (ID <= 0) {
				// extended and weighted picking
				if (settings.weightedPicking) {
					int size = settings.extendedPicking;

					ByteBuffer pixels = ByteBuffer.allocateDirect(
							size * size * 4).order(ByteOrder.nativeOrder());

					GLES20.glReadPixels(x - size / 2, height - y - size / 2,
							size, size, GLES20.GL_RGBA,
							GLES20.GL_UNSIGNED_BYTE, pixels);

					pixels.position(0);

					int[] b = new int[size * size * 4];
					for (int i = 0; i < b.length; i++) {
						b[i] = (pixels.get(i) & 0xFF) / 17;
					}

					int[] IDs = new int[size * size];
					for (int i = 0; i < IDs.length * 4; i += 4) {
						IDs[i / 4] = b[i];
						IDs[i / 4] += b[i + 1] * 16;
						IDs[i / 4] += b[i + 2] * 255;
					}

					int[] swapIDs = new int[size * size];
					int[] IDcount = new int[size * size];

					for (int i = 0; i < IDs.length; i++) {
						for (int j = 0; j < swapIDs.length; j++) {
							if (IDs[j] == -1)
								break;
							if (swapIDs[j] == 0) {
								int vX = i % size - size / 2;
								int vY = i / size - size / 2;

								IDcount[j] = (int) (size / Math.sqrt(vX * vX
										+ vY * vY));
								swapIDs[j] = IDs[i];
								break;
							}
							if (IDs[i] == swapIDs[j]) {
								int vX = i % size - size / 2;
								int vY = i / size - size / 2;

								IDcount[j] += size
										/ Math.sqrt(vX * vX + vY * vY);
								break;
							}
						}

					}

					int winner = 0;
					for (int i = 1; i < IDcount.length; i++) {
						if (IDcount[winner] < IDcount[i])
							winner = i;
					}

					ID = swapIDs[winner];
				}
				// extended but not weighted picking
				else {
					int size = settings.extendedPicking;

					ByteBuffer pixels = ByteBuffer.allocateDirect(
							size * size * 4).order(ByteOrder.nativeOrder());

					GLES20.glReadPixels(x - size / 2, height - y - size / 2,
							size, size, GLES20.GL_RGBA,
							GLES20.GL_UNSIGNED_BYTE, pixels);

					pixels.position(0);

					int[] b = new int[size * size * 4];
					for (int i = 0; i < b.length; i++) {
						b[i] = (pixels.get(i) & 0xFF) / 17;
					}

					int[] IDs = new int[size * size];
					for (int i = 0; i < IDs.length * 4; i += 4) {
						IDs[i / 4] = b[i];
						IDs[i / 4] += b[i + 1] * 16;
						IDs[i / 4] += b[i + 2] * 255;
					}

					int[] swapIDs = new int[size * size];
					int[] IDcount = new int[size * size];

					for (int i = 0; i < IDs.length; i++) {
						for (int j = 0; j < swapIDs.length; j++) {
							if (IDs[j] == -1)
								break;
							if (swapIDs[j] == 0) {
								IDcount[j]++;
								swapIDs[j] = IDs[i];

								break;
							}
							if (IDs[i] == swapIDs[j]) {
								IDcount[j]++;
								break;
							}
						}

					}

					int winner = 0;
					for (int i = 1; i < IDcount.length; i++) {
						if (IDcount[winner] < IDcount[i])
							winner = i;
					}

					ID = swapIDs[winner];
				}
			}
		}

		if (ID == -1)
			return;

		Identifier outID = new Identifier(null);
		sceneManager.searchColor(ID, scene, outID);

		GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

		if (picked) {
			pickingIdentifier = pickingID;
			waitforpick = false;
		} else {
			waitforpick = false;
			return;
		}
	}

	// ////////////////////////////////////////////////////////
	// /Sets up the needed FBO and RBO for color picking
	// ////////////////////////////////////////////////////////

	private void setupPickingRendering() {
		pickingFBOHandle = new int[1];
		pickingRBOHandle = new int[1];
		pickingDepthRBOHandle = new int[1];

		GLES20.glGenFramebuffers(1, pickingFBOHandle, 0);
		GLES20.glGenRenderbuffers(1, pickingRBOHandle, 0);
		GLES20.glGenRenderbuffers(1, pickingDepthRBOHandle, 0);

		GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, pickingFBOHandle[0]);

		GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, pickingRBOHandle[0]);
		GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_RGBA4,
				width, height);

		GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER,
				pickingDepthRBOHandle[0]);
		GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER,
				GLES20.GL_DEPTH_COMPONENT16, width, height);

		GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, 0);
		GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
	}

	// ////////////////////////////////////////////////////////
	// /Generates the texture used for picking (draws all models
	// /with special draw-method)
	// ////////////////////////////////////////////////////////

	private void drawPickingTexture() {
		GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, pickingFBOHandle[0]);

		GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER,
				GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_RENDERBUFFER,
				pickingRBOHandle[0]);

		GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER,
				GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER,
				pickingDepthRBOHandle[0]);

		int statusFBO = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
		if (statusFBO != GLES20.GL_FRAMEBUFFER_COMPLETE) {
			System.out.println("Framebuffer used for picking is incomplete!");
		}

		GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

		GLES20.glDisable(GLES20.GL_DITHER);
		GLES20.glDisable(GLES20.GL_BLEND);

		setAllHandlers(mPickingProgram);
		GLES20.glUseProgram(mPickingProgram);

		for (Identifier p : sceneManager.getAllObjects()) {
			if (!p.isVisible)
				continue;

			this.mTransformationMatrix = p.mTotalTransformationMatrix.clone();

			if (p.holdsLight) {
				continue;
			} else if (p.holdsModel3D) {
				this.drawModelForPicking(p.child, mPickingProgram, p.colorID);
			} else if (p.holdsModelGroup) {
				for (int j = 0; j < p.modelGroupChild.models.size(); j++) {
					this.drawModelForPicking(p.modelGroupChild.models.get(j),
							mPickingProgram, p.colorID);
				}
			} else if (p.holdsModelAnimation) {
				this.drawModelForPicking(this.getAnimationFrame(p),
						mPickingProgram, p.colorID);
			} else if (p.holdsText) {
				break;
			}
		}

		GLES20.glEnable(GLES20.GL_DITHER);
		GLES20.glEnable(GLES20.GL_BLEND);

		GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
	}

	// ////////////////////////////////////////////////////////
	// /Draws a Model3D for the pickingTexture - just the vertices and the
	// MVPMatrix will be passed to shader with a unique color
	// ////////////////////////////////////////////////////////

	private void drawModelForPicking(Model3D model, int usedProgram,
			int pickingColorID) {
		float[] mModelMatrix = new float[16];
		Matrix.setIdentityM(mModelMatrix, 0);

		// Matrix.multiplyMM(mModelMatrix, 0, model.mTransformationMatrix, 0,
		// mModelMatrix, 0);
		Matrix.multiplyMM(mModelMatrix, 0, mTransformationMatrix, 0,
				mModelMatrix, 0);

		// MVPMatrix = (Projection * View) * ModelMatrix
		Matrix.multiplyMM(mMVPMatrix, 0, mProjectionViewMatrix, 0,
				mModelMatrix, 0);
		// Give MVP Matrix to shader
		GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, mMVPMatrix, 0);

		int STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITH_TEXTURE_WITH_NORMAL;

		if (!model.textureCoordsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_TEXTURE_WITHOUT_NORMAL;

		if (!model.normalsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_NORMALS_WITH_TEXTURE;

		if (!model.normalsSet && !model.textureCoordsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_NORMALS_WITHOUT_TEXTURE;

		int blue = (int) (pickingColorID / 255);
		int rest = pickingColorID % 255;

		int green = rest / 16;
		rest = rest % 16;

		int red = rest;

		float outRed = ((float) red + 0.5f) / 16.0f;
		float outGreen = ((float) green + 0.5f) / 16.0f;
		float outBlue = ((float) blue + 0.5f) / 16.0f;

		float[] pColor = { outRed, outGreen, outBlue, 1.0f };

		int locPickCol = GLES20.glGetUniformLocation(usedProgram, "u_color");
		GLES20.glUniform4fv(locPickCol, 1, pColor, 0);

		if (!settings.useVBO) {
			// Give vertices to vertexshader
			model.mesh.bVertexBuffer
					.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
			GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT,
					false, STRIDE_SIZE, model.mesh.bVertexBuffer);
			GLES20.glEnableVertexAttribArray(aPositionHandle);

			// Draw everything
			GLES20.glDrawElements(GLES20.GL_TRIANGLES,
					model.bIndexBuffer.capacity(), GLES20.GL_UNSIGNED_SHORT,
					model.bIndexBuffer);
		} else {
			// Give verticebuffer to shader
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, model.mesh.vBuffer);

			GLES20.glEnableVertexAttribArray(0);
			GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false,
					4 * (3 + 3 + 2), 0);

			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, model.iBuffer);
			// Draw everything
			GLES20.glDrawElements(GLES20.GL_TRIANGLES, model.numIndices,
					GLES20.GL_UNSIGNED_SHORT, 0);

			// Unbind buffers
			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
		}
	}

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// /Functions for shadowmaps
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// /Sets up all needed buffers and textures for shadowmapping
	// ////////////////////////////////////////////////////////

	private void setupShadowMap() {
		int maxLights = settings.maxLights;

		if (maxLights >= 1) {

			depthTexture = new int[maxLights];
			depthFBO = new int[maxLights];
			depthRBO = new int[maxLights];

			GLES20.glGenFramebuffers(maxLights, depthFBO, 0);
			GLES20.glGenTextures(maxLights, depthTexture, 0);
			GLES20.glGenRenderbuffers(maxLights, depthRBO, 0);
		}

		for (int i = 0; i < maxLights; i++) {
			IntBuffer depthTexBuffer = ByteBuffer
					.allocateDirect(width * height * FLOAT_SIZE_BYTES)
					.order(ByteOrder.nativeOrder()).asIntBuffer();

			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, depthTexture[i]);

			GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
					GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
			GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
					GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
			GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
					GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
			GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
					GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);

			GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width,
					height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
					depthTexBuffer);

			GLES20.glBindBuffer(GLES20.GL_FRAMEBUFFER, depthFBO[i]);

			GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, depthRBO[i]);
			GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER,
					GLES20.GL_DEPTH_COMPONENT16, width, height);

			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
			GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
		}
	}

	// ////////////////////////////////////////////////////////
	// /draws the models specifically for the depth Texture
	// ////////////////////////////////////////////////////////

	private void drawModelForDepthTexture(Model3D model, int usedProgram,
			float[] lightLookAt) {
		float[] mModelMatrix = new float[16];
		Matrix.setIdentityM(mModelMatrix, 0);

		Matrix.multiplyMM(mModelMatrix, 0, mTransformationMatrix, 0,
				mModelMatrix, 0);

		// MVPMatrix = (Projection * View) * ModelMatrix
		Matrix.multiplyMM(mMVPMatrix, 0, mProjectionViewMatrix, 0,
				mModelMatrix, 0);
		// Give MVP Matrix to shader
		GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, mMVPMatrix, 0);

		int STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITH_TEXTURE_WITH_NORMAL;

		if (!model.textureCoordsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_TEXTURE_WITHOUT_NORMAL;

		if (!model.normalsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_NORMALS_WITH_TEXTURE;

		if (!model.normalsSet && !model.textureCoordsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_NORMALS_WITHOUT_TEXTURE;

		if (!settings.useVBO) {
			// Give vertices to vertexshader
			model.mesh.bVertexBuffer
					.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
			GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT,
					false, STRIDE_SIZE, model.mesh.bVertexBuffer);
			GLES20.glEnableVertexAttribArray(aPositionHandle);

			// Draw everything
			GLES20.glDrawElements(GLES20.GL_TRIANGLES,
					model.bIndexBuffer.capacity(), GLES20.GL_UNSIGNED_SHORT,
					model.bIndexBuffer);
		} else {
			// Give verticebuffer to shader
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, model.mesh.vBuffer);

			GLES20.glEnableVertexAttribArray(0);
			GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false,
					4 * (3 + 3 + 2), 0);

			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, model.iBuffer);
			// Draw everything
			GLES20.glDrawElements(GLES20.GL_TRIANGLES, model.numIndices,
					GLES20.GL_UNSIGNED_SHORT, 0);

			// Unbind buffers
			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
		}

	}

	private void drawShadowMap() {
		setAllHandlers(mDepthProgram);
		GLES20.glUseProgram(mDepthProgram);

		int i = 0;

		for (Identifier l : sceneManager.getAllLights()) {

			GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, depthFBO[i]);

			GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
					GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D,
					depthTexture[i], 0);

			GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER,
					GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER,
					depthRBO[i]);

			int statusFBO = GLES20
					.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
			if (statusFBO != GLES20.GL_FRAMEBUFFER_COMPLETE) {
				System.out
						.println("Framebuffer used for shadowmapping is incomplete!");
			}

			GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT
					| GLES20.GL_COLOR_BUFFER_BIT);

			drawLightForShadowMap(l.lightChild, mDepthProgram);

			for (Identifier p : sceneManager.getAllObjects()) {
				if (!p.isVisible)
					continue;

				this.mTransformationMatrix = p.mTotalTransformationMatrix
						.clone();

				float[] lightMVP = new float[16];

				float ratio = (float) Math.atan(l.lightChild.getAngle());
				float[] lightProjection = new float[16];
				Matrix.frustumM(lightProjection, 0, -ratio, ratio, 
													-ratio, ratio, 
													0.9f, 10000);

				float[] lightMV = new float[16];
				float[] mModelMatrix = new float[16];
				Matrix.setIdentityM(mModelMatrix, 0);

				Matrix.multiplyMM(mModelMatrix, 0, mTransformationMatrix, 0,
						mModelMatrix, 0);

				Matrix.multiplyMM(lightMV, 0, l.lightChild.getView(), 0,
						mModelMatrix, 0);

				Matrix.multiplyMM(lightMVP, 0, lightProjection, 0, lightMV, 0);

				int locMVP = GLES20.glGetUniformLocation(mDepthProgram,
						"l_mvp_matrix");
				GLES20.glUniformMatrix4fv(locMVP, 1, false, lightMVP, 0);

				this.mTransformationMatrix = p.mTotalTransformationMatrix
						.clone();

				if (p.holdsLight) {
					continue;
				} else if (p.holdsModel3D) {
					this.drawModelForDepthTexture(p.child, mDepthProgram,
							mViewMatrix);
				} else if (p.holdsModelGroup) {
					for (int j = 0; j < p.modelGroupChild.models.size(); j++) {
						this.drawModelForDepthTexture(
								p.modelGroupChild.models.get(j), mDepthProgram,
								mViewMatrix);
					}
				} else if (p.holdsModelAnimation) {
					this.drawModelForDepthTexture(this.getAnimationFrame(p),
							mDepthProgram, mViewMatrix);
				} else if (p.holdsText) {
				}
			}

			i++;
		}

		GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
	}

	// ////////////////////////////////////////////////////////
	// /Draws a light for the shadowMap
	// ////////////////////////////////////////////////////////

	private void drawLightForShadowMap(Light light, int usedProgram) {

		float[] halfplane = light.getPosition().clone();
		halfplane[2] += 1;
		VectorMethods.normalize(halfplane);

		int locDirection = GLES20.glGetUniformLocation(usedProgram,
				"u_light.direction");
		GLES20.glUniform3fv(locDirection, 1, light.getDirection(), 0);

		int locHalf = GLES20.glGetUniformLocation(usedProgram,
				"u_light.halfplane");
		GLES20.glUniform3fv(locHalf, 1, halfplane, 0);

		int locPosition = GLES20.glGetUniformLocation(usedProgram,
				"u_light.position");
		GLES20.glUniform3fv(locPosition, 1, light.getPosition(), 0);

		int locExponent = GLES20.glGetUniformLocation(usedProgram,
				"u_light.exponent");
		GLES20.glUniform1f(locExponent, light.getExponent());

		int locAngle = GLES20
				.glGetUniformLocation(usedProgram, "u_light.angle");
		GLES20.glUniform1f(locAngle, light.getAngle());

		int locDiffuse = GLES20.glGetUniformLocation(usedProgram,
				"u_light.diffuse_color");
		GLES20.glUniform4fv(locDiffuse, 1, light.getLightDiffuse(), 0);

		int locSpecular = GLES20.glGetUniformLocation(usedProgram,
				"u_light.specular_color");
		GLES20.glUniform4fv(locSpecular, 1, light.getLightSpecular(), 0);
	}

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// /Function to draw a text-object
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// /Draws a given text either 3D in the scene or flat (toCamera)
	// ////////////////////////////////////////////////////////

	private void drawText(Text text) {
		if (!text.toCamera) {
			float[] mModelMatrix = new float[16];
			Matrix.setIdentityM(mModelMatrix, 0);

			Matrix.multiplyMM(mModelMatrix, 0, mTransformationMatrix, 0,
					mModelMatrix, 0);

			// MVPMatrix = (Projection * View) * ModelMatrix
			Matrix.multiplyMM(mMVPMatrix, 0, mProjectionViewMatrix, 0,
					mModelMatrix, 0);
			// Give MVP Matrix to shader
			GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, mMVPMatrix, 0);
		} else {
			GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false,
					text.positionOnScreen, 0);
		}

		// (3+2)*4
		int STRIDE_SIZE = 20;

		int locTextCol = GLES20.glGetUniformLocation(mTextProgram, "u_color");
		GLES20.glUniform4fv(locTextCol, 1, text.color, 0);

		if (!settings.useVBO) {
			// Give vertices to vertexshader
			text.vertexBuffer.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
			GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT,
					false, STRIDE_SIZE, text.vertexBuffer);
			GLES20.glEnableVertexAttribArray(aPositionHandle);

			GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, text.font.ID);

			// Give texCoords to vertexshader
			text.vertexBuffer.position(3);
			GLES20.glVertexAttribPointer(aTextureHandle, 2, GLES20.GL_FLOAT,
					false, STRIDE_SIZE, text.vertexBuffer);
			GLES20.glEnableVertexAttribArray(aTextureHandle);

			GLES20.glDrawElements(GLES20.GL_TRIANGLES,
					text.indexBuffer.capacity(), GLES20.GL_UNSIGNED_SHORT,
					text.indexBuffer);
		} else {
			// Give verticebuffer to shader
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, text.vBuffer);

			GLES20.glEnableVertexAttribArray(0);
			GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false,
					STRIDE_SIZE, 0);

			GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, text.font.ID);

			GLES20.glEnableVertexAttribArray(2);
			GLES20.glVertexAttribPointer(2, 2, GLES20.GL_FLOAT, false,
					STRIDE_SIZE, FLOAT_SIZE_BYTES * 3);

			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, text.iBuffer);
			// Draw everything
			GLES20.glDrawElements(GLES20.GL_TRIANGLES, text.numIndices,
					GLES20.GL_UNSIGNED_SHORT, 0);

			// Unbind buffers
			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
		}
	}

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// /Functions for imagepostprocessing and fullscreenquads
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// /Sets up all needed buffers and textures for imagepostprocessing
	// ////////////////////////////////////////////////////////

	private void setupPostProcessing() {

		IntBuffer ippTexBuffer = ByteBuffer
				.allocateDirect(width * height * FLOAT_SIZE_BYTES)
				.order(ByteOrder.nativeOrder()).asIntBuffer();

		ippTexture = new int[1];
		ippFBO = new int[1];
		ippRBO = new int[1];

		GLES20.glGenFramebuffers(1, ippFBO, 0);
		GLES20.glGenTextures(1, ippTexture, 0);
		GLES20.glGenRenderbuffers(1, ippRBO, 0);

		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, ippTexture[0]);

		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
				GLES20.GL_CLAMP_TO_EDGE);
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
				GLES20.GL_CLAMP_TO_EDGE);
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
				GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
				GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);

		GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width,
				height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
				ippTexBuffer);

		GLES20.glBindBuffer(GLES20.GL_FRAMEBUFFER, ippFBO[0]);

		GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, ippRBO[0]);
		GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER,
				GLES20.GL_DEPTH_COMPONENT16, width, height);

		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
		GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

		if (settings.bloom) {
			IntBuffer bloomTexBuffer = ByteBuffer
					.allocateDirect(width * height * FLOAT_SIZE_BYTES)
					.order(ByteOrder.nativeOrder()).asIntBuffer();

			bloomTexture = new int[1];
			bloomFBO = new int[1];
			bloomRBO = new int[1];

			GLES20.glGenFramebuffers(1, bloomFBO, 0);
			GLES20.glGenTextures(1, bloomTexture, 0);
			GLES20.glGenRenderbuffers(1, bloomRBO, 0);

			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bloomTexture[0]);

			GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
					GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
			GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
					GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
			GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
					GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
			GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
					GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);

			GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width,
					height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
					bloomTexBuffer);

			GLES20.glBindBuffer(GLES20.GL_FRAMEBUFFER, bloomFBO[0]);

			GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, bloomRBO[0]);
			GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER,
					GLES20.GL_DEPTH_COMPONENT16, width, height);

			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
			GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
		}
	}

	private void postProcessing() {
		postProcessingTexture.ID = ippTexture[0];

		// Blur & Bloom
		if (settings.blur) {
			if (!settings.bloom) {
				GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, ippFBO[0]);
				GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
						GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D,
						ippTexture[0], 0);
				GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER,
						GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER,
						ippRBO[0]);

				int statusFBO = GLES20
						.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
				if (statusFBO != GLES20.GL_FRAMEBUFFER_COMPLETE) {
					System.out
							.println("Framebuffer used for image postprocessing is incomplete!");
				}
				GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT
						| GLES20.GL_COLOR_BUFFER_BIT);
			} else if (settings.bloom) {
				// Draw the entire scene with one color and use the result for
				// the blurring
				GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, bloomFBO[0]);
				GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
						GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D,
						bloomTexture[0], 0);
				GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER,
						GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER,
						bloomRBO[0]);

				int statusFBO = GLES20
						.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
				if (statusFBO != GLES20.GL_FRAMEBUFFER_COMPLETE) {
					System.out
							.println("Framebuffer used for bloom is incomplete!");
				}

				setAllHandlers(mBloomProgram);
				GLES20.glUseProgram(mBloomProgram);

				this.drawBloomTexture(mBloomProgram);

				bloomResultTexture.ID = bloomTexture[0];

				GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

				GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, bloomFBO[0]);
				GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
						GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D,
						bloomTexture[0], 0);
				GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER,
						GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER,
						bloomRBO[0]);

				statusFBO = GLES20
						.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
				if (statusFBO != GLES20.GL_FRAMEBUFFER_COMPLETE) {
					System.out
							.println("Framebuffer used for bloom is incomplete!");
				}

				GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT
						| GLES20.GL_COLOR_BUFFER_BIT);
			}

			// Use blur shaders

			setAllHandlers(mBlurProgram);
			GLES20.glUseProgram(mBlurProgram);

			gaussDirection = 0;

			int gDirLoc0 = GLES20.glGetUniformLocation(mBlurProgram,
					"u_gaussDirection");
			GLES20.glUniform1f(gDirLoc0, gaussDirection);

			int uBlurLoc = GLES20.glGetUniformLocation(mBlurProgram,
					"u_blurringMode");
			GLES20.glUniform1f(uBlurLoc, settings.blurringMode);

			int uOffSetLoc = GLES20.glGetUniformLocation(mBlurProgram,
					"u_offSet");
			GLES20.glUniform1f(uOffSetLoc, settings.blurRadius);

			int sigmaLoc = GLES20.glGetUniformLocation(mBlurProgram, "u_sigma");
			GLES20.glUniform1f(sigmaLoc, settings.sigma);

			int widthLoc = GLES20.glGetUniformLocation(mBlurProgram, "width");
			GLES20.glUniform1i(widthLoc, width);

			int heightLoc = GLES20.glGetUniformLocation(mBlurProgram, "height");
			GLES20.glUniform1i(heightLoc, height);

			if (settings.bloom)
				drawFullScreenQuad(bloomResultTexture, mBlurProgram);
			else
				drawFullScreenQuad(postProcessingTexture, mBlurProgram);

			if (settings.blurringMode == 1) {
				GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT
						| GLES20.GL_COLOR_BUFFER_BIT);
				gaussDirection = 1;
				GLES20.glUniform1f(gDirLoc0, gaussDirection);
				if (settings.bloom)
					drawFullScreenQuad(bloomResultTexture, mBlurProgram);
				else
					drawFullScreenQuad(postProcessingTexture, mBlurProgram);
			}

			GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
		}

		// Change shaders to fullScreenQuadShaders and draw the
		// fullscreenquad with the ppTexture
		setAllHandlers(mFullScreenProgram);
		GLES20.glUseProgram(mFullScreenProgram);

		GLES20.glEnable(GLES20.GL_BLEND);
		GLES20.glDepthMask(false);
		GLES20.glDisable(GLES20.GL_CULL_FACE);

		// GLES20.glBlendEquation(GLES20.GL_FUNC_ADD);
		GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
		if (settings.bloom) {
			drawFullScreenQuad(bloomResultTexture, mFullScreenProgram);
			GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT);
		}

		drawFullScreenQuad(postProcessingTexture, mFullScreenProgram);
		GLES20.glDisable(GLES20.GL_BLEND);
		GLES20.glDepthMask(true);
		GLES20.glEnable(GLES20.GL_CULL_FACE);
	}

	private void setFullScreenQuad() {

		fullScreenQuadvBuffer = ByteBuffer
				.allocateDirect(
						fullScreenQuadVertices.length * FLOAT_SIZE_BYTES)
				.order(ByteOrder.nativeOrder()).asFloatBuffer();
		fullScreenQuadvBuffer.put(fullScreenQuadVertices);
		fullScreenQuadvBuffer.position(0);

		fullScreenQuadiBuffer = ByteBuffer
				.allocateDirect(fullScreenQuadIndices.length * 2)
				.order(ByteOrder.nativeOrder()).asShortBuffer();
		fullScreenQuadiBuffer.put(fullScreenQuadIndices);
		fullScreenQuadiBuffer.position(0);

		if (settings.useVBO) {
			int[] buffers = new int[2];
			GLES20.glGenBuffers(2, buffers, 0);

			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[0]);
			GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER,
					fullScreenQuadvBuffer.capacity() * FLOAT_SIZE_BYTES,
					fullScreenQuadvBuffer, GLES20.GL_STATIC_DRAW);

			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
			GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER,
					fullScreenQuadiBuffer.capacity() * SHORT_SIZE_BYTES,
					fullScreenQuadiBuffer, GLES20.GL_STATIC_DRAW);

			fullscreenQuadVertexBuffer = buffers[0];
			fullScreenQuadIndexBuffer = buffers[1];
			numFullScreenQuadIndices = fullScreenQuadiBuffer.capacity();
		}
	}

	// ////////////////////////////////////////////////////////
	// /Generates the texture used for picking (draws all models
	// /with special draw-method)
	// ////////////////////////////////////////////////////////

	private void drawBloomTexture(int usedProgram) {
		GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);

		int num = 0;

		for (Identifier p : sceneManager.getAllLights()) {
			if (p.holdsLight) {
				this.drawLight(p.lightChild, usedProgram, num);
				num++;
			}
		}

		for (Identifier p : sceneManager.getAllObjects()) {
			if (!p.isVisible)
				continue;

			this.mTransformationMatrix = p.mTotalTransformationMatrix.clone();

			if (p.holdsLight) {
				continue;
			} else if (p.holdsModel3D) {
				this.drawModelForBloom(p.child, usedProgram);
			} else if (p.holdsModelGroup) {
				for (int j = 0; j < p.modelGroupChild.models.size(); j++) {
					this.drawModelForBloom(p.modelGroupChild.models.get(j),
							usedProgram);
				}
			} else if (p.holdsModelAnimation) {
				this.drawModelForBloom(this.getAnimationFrame(p), usedProgram);
			} else if (p.holdsText) {
				break;
			}
		}
	}

	// ////////////////////////////////////////////////////////
	// /Draws a Model3D for the pickingTexture - just the vertices and the
	// MVPMatrix will be passed to shader with a unique color
	// ////////////////////////////////////////////////////////

	private void drawModelForBloom(Model3D model, int usedProgram) {
		float[] mModelMatrix = new float[16];
		Matrix.setIdentityM(mModelMatrix, 0);

		// Matrix.multiplyMM(mModelMatrix, 0, model.mTransformationMatrix, 0,
		// mModelMatrix, 0);
		Matrix.multiplyMM(mModelMatrix, 0, mTransformationMatrix, 0,
				mModelMatrix, 0);

		// MVPMatrix = (Projection * View) * ModelMatrix
		Matrix.multiplyMM(mMVPMatrix, 0, mProjectionViewMatrix, 0,
				mModelMatrix, 0);
		// Give MVP Matrix to shader
		GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, mMVPMatrix, 0);

		int locAmbient2 = GLES20.glGetUniformLocation(usedProgram,
				"v_material.ambient_color");
		GLES20.glUniform4fv(locAmbient2, 1, model.getMaterialAmbient(), 0);

		int locDiffuse2 = GLES20.glGetUniformLocation(usedProgram,
				"v_material.diffuse_color");
		GLES20.glUniform4fv(locDiffuse2, 1, model.getMaterialDiffuse(), 0);

		int locSpecular2 = GLES20.glGetUniformLocation(usedProgram,
				"v_material.specular_color");
		GLES20.glUniform4fv(locSpecular2, 1, model.getMaterialSpecular(), 0);

		int locExponent2 = GLES20.glGetUniformLocation(usedProgram,
				"v_material.specular_exponent");
		GLES20.glUniform1f(locExponent2, model.getMaterialExponent());

		int STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITH_TEXTURE_WITH_NORMAL;

		if (!model.textureCoordsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_TEXTURE_WITHOUT_NORMAL;

		if (!model.normalsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_NORMALS_WITH_TEXTURE;

		if (!model.normalsSet && !model.textureCoordsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_NORMALS_WITHOUT_TEXTURE;

		// Bloomcolor
		float[] pColor = { 0.0f, 0.0f, 0.0f, 1.0f };

		int locPickCol = GLES20.glGetUniformLocation(usedProgram, "u_color");
		GLES20.glUniform4fv(locPickCol, 1, pColor, 0);

		GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, mMVPMatrix, 0);

		float[] mModelViewMatrix = new float[16];
		Matrix.setIdentityM(mModelViewMatrix, 0);
		Matrix.multiplyMM(mModelViewMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
		float[] mInvertedViewMatrix = new float[16];
		Matrix.setIdentityM(mInvertedViewMatrix, 0);
		Matrix.invertM(mInvertedViewMatrix, 0, mModelViewMatrix, 0);
		float[] mInvertedTransposedViewMatrix = new float[16];
		Matrix.setIdentityM(mInvertedTransposedViewMatrix, 0);
		Matrix.transposeM(mInvertedTransposedViewMatrix, 0,
				mInvertedViewMatrix, 0);

		GLES20.glUniformMatrix4fv(uInvertedViewMatrixHandle, 1, false,
				mInvertedTransposedViewMatrix, 0);

		if (!settings.useVBO) {
			// Give vertices to vertexshader
			model.mesh.bVertexBuffer
					.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
			GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT,
					false, STRIDE_SIZE, model.mesh.bVertexBuffer);
			GLES20.glEnableVertexAttribArray(aPositionHandle);

			if (model.normalsSet) {
				// Give normals to vertexshader
				model.mesh.bVertexBuffer
						.position(TRIANGLE_VERTICES_DATA_NOR_OFFSET);
				GLES20.glVertexAttribPointer(aNormalHandle, 3, GLES20.GL_FLOAT,
						false, STRIDE_SIZE, model.mesh.bVertexBuffer);
				GLES20.glEnableVertexAttribArray(aNormalHandle);
			}

			// Draw everything
			GLES20.glDrawElements(GLES20.GL_TRIANGLES,
					model.bIndexBuffer.capacity(), GLES20.GL_UNSIGNED_SHORT,
					model.bIndexBuffer);
		} else {
			// Give verticebuffer to shader
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, model.mesh.vBuffer);

			GLES20.glEnableVertexAttribArray(0);
			GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false,
					4 * (3 + 3 + 2), 0);

			if (model.normalsSet) {
				GLES20.glEnableVertexAttribArray(1);
				GLES20.glVertexAttribPointer(1, 3, GLES20.GL_FLOAT, false,
						STRIDE_SIZE, FLOAT_SIZE_BYTES
								* TRIANGLE_VERTICES_DATA_NOR_OFFSET);
			}

			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, model.iBuffer);
			// Draw everything
			GLES20.glDrawElements(GLES20.GL_TRIANGLES, model.numIndices,
					GLES20.GL_UNSIGNED_SHORT, 0);

			// Unbind buffers
			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
		}
	}

	private void drawFullScreenQuad(Texture tex, int usedProgram) {
		float[] mModelMatrix = new float[16];
		Matrix.setIdentityM(mModelMatrix, 0);

		GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, mModelMatrix, 0);

		if (!settings.useVBO) {
			fullScreenQuadvBuffer.position(0);
			GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT,
					false, 5 * 4, fullScreenQuadvBuffer);
			GLES20.glEnableVertexAttribArray(aPositionHandle);

			int texture = GLES20.glGetUniformLocation(usedProgram,
					"renderTexture");
			GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.ID);
			GLES20.glUniform1i(texture, 0);

			// Give texCoords to vertexshader
			fullScreenQuadvBuffer.position(3);
			GLES20.glVertexAttribPointer(aTextureHandle, 2, GLES20.GL_FLOAT,
					false, 5 * 4, fullScreenQuadvBuffer);
			GLES20.glEnableVertexAttribArray(aTextureHandle);

			GLES20.glDrawElements(GLES20.GL_TRIANGLES,
					fullScreenQuadiBuffer.capacity(), GLES20.GL_UNSIGNED_SHORT,
					fullScreenQuadiBuffer);
		} else {
			// Give verticebuffer to shader
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER,
					fullscreenQuadVertexBuffer);

			// vertices
			GLES20.glEnableVertexAttribArray(0);
			GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false,
					4 * (3 + 2), 0);

			// Texture and texturecoordinates
			int texture = GLES20.glGetUniformLocation(usedProgram,
					"renderTexture");
			GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
			GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.ID);
			GLES20.glUniform1i(texture, 0);

			GLES20.glEnableVertexAttribArray(2);
			GLES20.glVertexAttribPointer(2, 2, GLES20.GL_FLOAT, false,
					4 * (3 + 2), 3 * FLOAT_SIZE_BYTES);

			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER,
					fullScreenQuadIndexBuffer);
			// Draw everything
			GLES20.glDrawElements(GLES20.GL_TRIANGLES,
					numFullScreenQuadIndices, GLES20.GL_UNSIGNED_SHORT, 0);

			// Unbind buffers
			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
		}
	}

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// /draws a model specifically for the outline
	// ////////////////////////////////////////////////////////

	private void drawModelForOutline(Model3D model, int usedProgram) {
		float[] mModelMatrix = new float[16];
		Matrix.setIdentityM(mModelMatrix, 0);

		Matrix.multiplyMM(mModelMatrix, 0, mTransformationMatrix, 0,
				mModelMatrix, 0);

		float[] mResize = new float[16];
		Matrix.setIdentityM(mResize, 0);
		Matrix.scaleM(mResize, 0, settings.outLineSize, settings.outLineSize,
				settings.outLineSize);

		Matrix.multiplyMM(mModelMatrix, 0, mModelMatrix, 0, mResize, 0);

		// MVPMatrix = (Projection * View) * ModelMatrix
		Matrix.multiplyMM(mMVPMatrix, 0, mProjectionViewMatrix, 0,
				mModelMatrix, 0);
		// Give MVP Matrix to shader
		GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, mMVPMatrix, 0);

		int STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITH_TEXTURE_WITH_NORMAL;

		if (!model.textureCoordsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_TEXTURE_WITHOUT_NORMAL;

		if (!model.normalsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_NORMALS_WITH_TEXTURE;

		if (!model.normalsSet && !model.textureCoordsSet)
			STRIDE_SIZE = TRIANGLE_VERTICES_DATA_STRIDE_BYTES_WITHOUT_NORMALS_WITHOUT_TEXTURE;

		if (!settings.useVBO) {
			// Give vertices to vertexshader
			model.mesh.bVertexBuffer
					.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
			GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT,
					false, STRIDE_SIZE, model.mesh.bVertexBuffer);
			GLES20.glEnableVertexAttribArray(aPositionHandle);

			// Draw everything
			GLES20.glDrawElements(GLES20.GL_TRIANGLES,
					model.bIndexBuffer.capacity(), GLES20.GL_UNSIGNED_SHORT,
					model.bIndexBuffer);
		} else {
			// Give verticebuffer to shader
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, model.mesh.vBuffer);

			GLES20.glEnableVertexAttribArray(0);
			GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false,
					4 * (3 + 3 + 2), 0);

			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, model.iBuffer);
			// Draw everything
			GLES20.glDrawElements(GLES20.GL_TRIANGLES, model.numIndices,
					GLES20.GL_UNSIGNED_SHORT, 0);

			// Unbind buffers
			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
		}
	}

	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////

	// ////////////////////////////////////////////////////////
	// /determines which frame to draw for a given animation-identifier. Used in
	// all drawing-functions. Returns a Model3D.
	// ////////////////////////////////////////////////////////
	private Model3D getAnimationFrame(Identifier p) {
		if (!p.animationChild.byTime) {
			int currentFrame = p.animationChild.localFrameCount;
			if (!p.animationChild.stopped) {
				if (!p.animationChild.paused) {
					int startFrame = this.frameCount - p.animationChild.start;

					if (startFrame >= 0) {
						if (p.animationChild.FPFcount == p.animationChild.delayPerFrame) {
							p.animationChild.FPFcount = 0;

							if (p.animationChild.loop) {
								if (currentFrame >= (p.animationChild.duration - 2))
									p.animationChild.localFrameCount = 0;
							} else {
								if (currentFrame >= (p.animationChild.duration - 2))
									p.animationChild.stopped = true;
							}
							p.animationChild.localFrameCount++;
							return p.animationChild.frames.get(currentFrame);

						} else {
							p.animationChild.FPFcount++;
							return p.animationChild.frames.get(currentFrame);
						}
					}

				} else {
					return p.animationChild.frames.get(currentFrame);
				}
			}
		} else if (p.animationChild.byTime) {

			if (p.animationChild.startTime == 0)
				p.animationChild.startTime = globalTime;

			if (!p.animationChild.stopped) {
				if (!p.animationChild.paused) {

					if (p.animationChild.firstFrameAfterUnpaused) {
						p.animationChild.startTime = p.animationChild.timePaused;
						p.animationChild.firstFrameAfterUnpaused = false;
					}
					p.animationChild.currentTime = globalTime
							- p.animationChild.startTime;

					int currentFrame = (int) (p.animationChild.currentTime * p.animationChild.fps);

					if (currentFrame >= p.animationChild.frames.size()) {
						currentFrame = 0;
						p.animationChild.startTime = globalTime;
						if (!p.animationChild.loop)
							p.animationChild.stopped = true;
					}

					return p.animationChild.frames.get(currentFrame);

				} else {
					if (p.animationChild.firstFrameAfterPaused) {
						p.animationChild.currentTime = globalTime
								- p.animationChild.startTime;
						p.animationChild.timePaused = globalTime;
						p.animationChild.firstFrameAfterPaused = false;
					}

					int currentFrame = (int) (p.animationChild.currentTime * p.animationChild.fps);
					
					if (currentFrame >= p.animationChild.frames.size()) {
						currentFrame = 0;
						p.animationChild.timePaused = 0;
					}

					return p.animationChild.frames.get(currentFrame);
				}
			}
		}
		return null;
	}
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
	// ////////////////////////////////////////////////////////
}